﻿namespace Arms.Domain
{
    using System;

    /// <summary>
    /// Base class for all domain entities
    /// </summary>
    public abstract class Entity
    {
        #region Properties

        private int? _hashCode;

        /// <summary>
        /// Unique Id of the entity.
        /// </summary>
        public int ID { get; set; }
        /// <summary>
        /// Checks if entity is transient (without identity)
        /// </summary>
        public virtual bool IsTransient
        {
            get { return this.ID <= 0; }
        }
        /// <summary>
        /// Determines whether this entity is active.
        /// </summary>
        public virtual bool IsActive
        {
            get { return this.ActiveState != EntityActiveState.Inactive || this.ActiveState != EntityActiveState.Deleted ; }
        }
        /// <summary>
        /// Determines whether this entity is deleted.
        /// </summary>
        public virtual bool IsDeleted
        {
            get { return this.ActiveState != EntityActiveState.Active || this.ActiveState != EntityActiveState.Inactive; }
        }
        /// <summary>
        /// Get the true type of this entity.
        /// </summary>
        public Type UnproxiedType
        {
            get { return this.GetType(); }
        }
        /// <summary>
        /// State of this entity.
        /// </summary>
        public EntityActiveState ActiveState { get; set; }
        /// <summary>
        /// Date of creation.
        /// </summary>
        public DateTime? Created { get; set; }
        /// <summary>
        /// Last updated.
        /// </summary>
        public DateTime? Updated { get; set; }
        /// <summary>
        /// User who created this entity.
        /// </summary>
        public int CreatedUserID { get; set; }
        /// <summary>
        /// User who last updated this entity.
        /// </summary>
        public int UpdatedUserID { get; set; }
        #endregion

        #region Members

        /// <summary>
        /// Determines whether this entity equals another entity.
        /// </summary>
        /// <param name="otherEntity"></param>
        /// <returns></returns>
        public virtual bool Equals(Entity otherEntity)
        {
            if (otherEntity == null)
            {
                return false;
            }

            if (!this.IsTransient && !otherEntity.IsTransient && Object.Equals(this.ID, otherEntity.ID))
            {
                var type = this.UnproxiedType;
                var otherType = otherEntity.UnproxiedType;
                return type.IsAssignableFrom(otherType) || otherType.IsAssignableFrom(type);
            }

            return false;
        }

        ///// <summary>
        ///// Generates a new unique identity for this entity.
        ///// </summary>
        //public virtual void GenerateIdentity()
        //{
        //    if (this.IsTransient)
        //    {
        //        this.ID = IdentityGenerator.NewSequentialGuid();
        //    }
        //}

        #endregion

        #region Overrides

        /// <summary>
        /// <see cref="M:System.Object.Equals"/>
        /// </summary>
        /// <param name="obj"><see cref="M:System.Object.Equals"/></param>
        /// <returns><see cref="M:System.Object.Equals"/></returns>
        public override bool Equals(object obj)
        {
            if (obj == null || !(obj is Entity))
            {
                return false;
            }

            if (Object.ReferenceEquals(this, obj))
            {
                return true;
            }

            Entity item = (Entity)obj;

            if (item.IsTransient || this.IsTransient)
            {
                return false;
            }
            else
            {
                return item.ID == this.ID;
            }
        }
        /// <summary>
        /// <see cref="M:System.Object.GetHashCode"/>
        /// </summary>
        /// <returns><see cref="M:System.Object.GetHashCode"/></returns>
        public override int GetHashCode()
        {
            if (!this.IsTransient)
            {
                if (!_hashCode.HasValue)
                {
                    // XOR for random distribution
                    // http://blogs.msdn.com/b/ericlippert/archive/2011/02/28/guidelines-and-rules-for-gethashcode.aspx
                    _hashCode = this.ID.GetHashCode() ^ 31;
                }

                return _hashCode.Value;
            }

            return base.GetHashCode();
        }
        /// <summary>
        /// Return the name of this entitie's unproxied type
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.UnproxiedType.ToString();
        }
        /// <summary>
        /// Determines whether the left entity equals the right entity.
        /// </summary>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns></returns>
        public static bool operator ==(Entity left, Entity right)
        {
            if (Object.Equals(left, null))
            {
                return (Object.Equals(right, null)) ? true : false;
            }

            return left.Equals(right);
        }
        /// <summary>
        /// Determines whether the left entity !equal the right entity.
        /// </summary>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns></returns>
        public static bool operator !=(Entity left, Entity right)
        {
            return !(left == right);
        }

        #endregion
    }
}
